﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Mvc;
using System.Web.Mvc.Html;

namespace AnyGrid.Renderers.HtmlTable
{
    public class HtmlTableRenderer : BaseGridRenderer
    {
        protected GridViewModel Grid { get; set; }
        internal HtmlTableRenderContext RenderContext { get; set; }

        public override string RenderHtml(GridViewModel grid, IDictionary<string, object> htmlAttributes)
        {
            if (grid == null)
            {
                throw new ArgumentNullException("grid");
            }
            this.Grid = grid;
            var idFieldName = IdColumn(grid.GridContext.RowModelMetadata);
            Func<ModelMetadata, object> keyGetter = mm => mm.Properties.Where(p => p.PropertyName == idFieldName).Select(p => p.Model).SingleOrDefault();
            this.RenderContext = new HtmlTableRenderContext
            {
                DeleteLinkText = Properties.Resources.DeleteLinkDisplayText,
                DetailsLinkText = Properties.Resources.DetailsLinkDisplayText,
                Grid = grid,
                InsertLinkText = Properties.Resources.InsertLinkDisplayText,
                KeyGetter = keyGetter,
                ShowActionsColumn = ShowActionsColumn(),
                UpdateLinkText = Properties.Resources.UpdateLinkDisplayText
            };
            return base.RenderHtml(grid, htmlAttributes) + RenderHtmlTable(grid, htmlAttributes);
        }

        public string RenderHtmlTable(GridViewModel grid, IDictionary<string, object> htmlAttributes)
        {
            var table = TableTagBuilder(grid, htmlAttributes);
            var thead = new TagBuilder("thead");
            thead.InnerHtml = RenderHeaders();
            var tbody = new TagBuilder("tbody");
            tbody.InnerHtml = RenderBody();
            table.InnerHtml = thead.ToString() + tbody.ToString();
            return table.ToString();
        }

        private static IEnumerable<ModelMetadata> PropertiesToDisplay(ModelMetadata containerObject)
        {
            return containerObject.Properties.Where(p => p.ShowForDisplay);
        }

        private static string RenderActionsHeader()
        {
            return "<th></th>";
        }

        private string RenderBody()
        {
            if (RenderContext.Grid.FirstPageData == null)
            {
                return string.Empty;
            }
            var data = Grid.FirstPageData.Cast<object>();
            var result = new StringBuilder();
            foreach (var row in data)
            {
                result.Append(RenderRow(row));                
            }
            return result.ToString();
        }

        private static string RenderCell(ModelMetadata value)
        {
            return string.Concat("<td>", RenderCellValue(value), "</td>");
        }

        private static string RenderCellValue(ModelMetadata value)
        {
            return value.Model.ToString();
        }

        private string RenderHeaders()
        {
            var actionsHeader = RenderContext.ShowActionsColumn ? RenderActionsHeader() : string.Empty;
            var propertiesToDisplay = PropertiesToDisplay(Grid.GridContext.RowModelMetadata);
            var columnHeaders = string.Join(string.Empty, propertiesToDisplay.Select(p => RenderHeader(p.GetDisplayName())));
            return actionsHeader + columnHeaders;
        }

        private static string RenderHeader(string displayName)
        {
            return string.Concat("<th>", displayName, "</th>");
        }

        private string RenderRow(object row)
        {
            var tr = new TagBuilder("tr");
            var mm = Grid.GridContext.MetadataProvider.GetMetadataForType(() => row, row.GetType());
            var actions = RenderContext.ShowActionsColumn ? RenderRowActions(mm) : string.Empty;
            tr.InnerHtml = actions + string.Join("", PropertiesToDisplay(mm).Select(p => RenderCell(p)));
            return tr.ToString();
        }

        private string RenderRowActions(ModelMetadata rowModelMetadata)
        {
            Func<string, string, object, string> actionRenderer = (actionName, displayText, idValue) =>
            {
                if (string.IsNullOrEmpty(actionName))
                {
                    return string.Empty;
                }
                return Grid.GridContext.HtmlHelper.ActionLink(displayText, actionName, new Dictionary<string, object> { { "Id", idValue } }).ToString();
            };
            var id = RenderContext.KeyGetter(rowModelMetadata);
            var details = actionRenderer(RenderContext.DetailsLinkText, Grid.AllowedOperations.DetailsActionName, id);
            var insert = actionRenderer(RenderContext.InsertLinkText, Grid.AllowedOperations.InsertActionName, id);
            var update = actionRenderer(RenderContext.UpdateLinkText, Grid.AllowedOperations.UpdateActionName, id);
            var delete = actionRenderer(RenderContext.DeleteLinkText, Grid.AllowedOperations.DeleteActionName, id);
            return string.Concat("<td>", details, " ", insert, " ", update, " ", delete, " ", "</td>");
        }

        private bool ShowActionsColumn()
        {
            if (Grid.AllowedOperations == null)
            {
                return false;
            }
            return (!(string.IsNullOrEmpty(Grid.AllowedOperations.DeleteActionName)
                                  && string.IsNullOrEmpty(Grid.AllowedOperations.DetailsActionName)
                                  && string.IsNullOrEmpty(Grid.AllowedOperations.InsertActionName)
                                  && string.IsNullOrEmpty(Grid.AllowedOperations.UpdateActionName)))
                                || (Grid.AllowedOperations.CustomActions != null && Grid.AllowedOperations.CustomActions.Any());
        }
    }
}
